package org.csu.softwaremetrics_demo.metric.tradition;

import org.csu.softwaremetrics_demo.entity.common.TraditionMetricSingleClass;
import org.csu.softwaremetrics_demo.entity.result.Result;
import org.csu.softwaremetrics_demo.metric.Metric;
import org.csu.softwaremetrics_demo.metric.ck.DIT;
import org.csu.softwaremetrics_demo.util.ColorLoggerUtils;
import org.eclipse.jdt.core.dom.*;

/**
 * @Description: LOComment(Comment Percentage) 代码注释百分比
 * @Author: Jukomu
 * @Package: org.csu.softwaremetrics_demo.metric.tradition
 * @Project: SoftwareMetrics_demo
 * @name: LOComment
 * @Date: 2025/4/8-19:14
 * @Filename: LOComment
 */
public class LOComment extends ASTVisitor implements Metric<TraditionMetricSingleClass> {
    private static final ColorLoggerUtils LOGGER = ColorLoggerUtils.getLogger(DIT.class);

    private int lenOfComment;   // 注释总行数
    private CompilationUnit compilationUnit;
    private int lineCommentLines;    // 单行注释行数
    private int blockCommentLines;   // 块注释行数
    private int javadocLines;       // Javadoc 注释行数

    private boolean flag = false;

    /**
     * 构造函数，初始化注释行数统计变量
     */
    public LOComment() {
        lenOfComment = 0;
        lineCommentLines = 0;
        blockCommentLines = 0;
        javadocLines = 0;
    }

    /**
     * 访问单行注释节点的方法
     *
     * @param node 当前访问的单行注释节点
     * @return 返回 false 表示不再继续遍历子节点
     */
    @Override
    public boolean visit(LineComment node) {
        lineCommentLines += calculateLineCount(node);
        return false;
    }

    /**
     * 访问块注释节点的方法
     *
     * @param node 当前访问的块注释节点
     * @return 返回 false 表示不再继续遍历子节点
     */
    @Override
    public boolean visit(BlockComment node) {
        blockCommentLines += calculateLineCount(node);
        return false;
    }

    /**
     * 处理 Javadoc 注释节点的方法
     *
     * @param node 当前访问的 Javadoc 注释节点
     * @return 返回 false 表示不再继续遍历子节点
     */
    @Override
    public boolean visit(Javadoc node) {
        if (flag) {
            return false;
        }
        javadocLines += calculateLineCount(node);
        return false;
    }

    /**
     * 访问 CompilationUnit 节点的方法。
     *
     * <p>
     * Javadoc注释会在AST中作为单独的节点出现，因此需要单独处理
     * CompilationUnit 节点的注释列表也包含Javadoc注释
     * 当CompilationUnit 节点的注释列表遍历完成后，继续遍历AST时会遇到已经统计过的Javadoc注释节点
     * 因此需要设置一个标志位flag，当遍历到Javadoc注释节点时，如果flag为true，则不再统计Javadoc注释
     * <p/>
     * <p>
     * 该方法覆盖了父类或接口中的 visit 方法
     * 方法返回一个布尔值，用于控制是否继续遍历子节点
     * 方法内部首先遍历 CompilationUnit 节点的注释列表，并对每个注释节点调用 accept 方法
     * 然后将内部标志 flag 设置为 true
     * <p/>
     *
     * @param node 当前访问的 CompilationUnit 节点
     * @return 返回 true 表示继续遍历其他子节点
     */
    @Override
    public boolean visit(CompilationUnit node) {
        node.getCommentList().forEach(comment -> ((Comment) comment).accept(this));
        flag = true;
        return true; // 继续遍历其他子节点
    }

    /**
     * 计算注释的行数
     *
     * @param comment 当前处理的注释节点
     * @return 注释的行数
     */
    private int calculateLineCount(Comment comment) {
        int startPos = comment.getStartPosition();
        int endPos = startPos + comment.getLength() - 1;
        int startLine = compilationUnit.getLineNumber(startPos);
        int endLine = compilationUnit.getLineNumber(endPos);
        return endLine - startLine + 1;
    }

    /**
     * 触发 Javadoc 注释的访问
     *
     * @param javadoc 当前处理的 Javadoc 注释节点
     */
    private void processJavadoc(Javadoc javadoc) {
        if (javadoc != null) {
            javadoc.accept(this);
        }
    }

    /**
     * 获取单行注释行数
     *
     * @return 单行注释行数
     */
    public int getLineCommentLines() {
        return lineCommentLines;
    }

    /**
     * 获取块注释行数
     *
     * @return 块注释行数
     */
    public int getBlockCommentLines() {
        return blockCommentLines;
    }

    /**
     * 获取 Javadoc 注释行数
     *
     * @return Javadoc 注释行数
     */
    public int getJavadocLines() {
        return javadocLines;
    }

    /**
     * 获取注释总行数
     *
     * @return 注释总行数
     */
    public int getTotalLines() {
        return lineCommentLines + blockCommentLines + javadocLines;
    }

    /**
     * 执行注释统计的方法
     *
     * @param cu           当前编译单元
     * @param singleMetric 统计结果对象
     * @param result       结果对象
     */
    @Override
    public void execute(CompilationUnit cu, TraditionMetricSingleClass singleMetric, Result result) {
        compilationUnit = cu;
        cu.accept(this);
    }

    /**
     * 设置统计结果到 singleMetric 对象中
     *
     * @param singleMetric 统计结果对象
     */
    @Override
    public void setSingleMetric(TraditionMetricSingleClass singleMetric) {
        if (singleMetric != null) {
            singleMetric.setLineCommentLines(lineCommentLines);
            singleMetric.setBlockCommentLines(blockCommentLines);
            singleMetric.setJavadocLines(javadocLines);
            lenOfComment = lineCommentLines + blockCommentLines + javadocLines;
            singleMetric.setLenOfComment(lenOfComment);
        } else {
            LOGGER.error("Cannot set lenOfComment metric on null object");
        }
    }
}
